import { Engine } from "../../core/Engine";
import Logger from "../../core/utils/Logger";
import { getNumFloor, getRandom } from "../../core/utils/utils";
import { EVENT_NAME, GAME_DATA, TIMER_TASK_TAG } from "../GameConstant";
import { dataManager } from "./DataManager";
import { BELONG_TYPE, MapData } from "./MapData";
import { ATTACK_OR_DEFENSE } from "./SoldierData";

export interface AttackCityInfo {
    cityId: string;
    defenseCityId: string;
    targetCityIds: string[];
}

class EnemyAI {
    public static instance = new EnemyAI();

    private defenseCitys: Set<string> = new Set<string>(); // 防守城
    private attackCitys: Set<AttackCityInfo> = new Set<AttackCityInfo>(); // 集结城
    // private attackTargetCitys: Set<string> = new Set<string>(); // 攻击目标城

    private noBuildingCitys: string[] = []; // 缺少建筑的城池
    private buildIdRatio: { [buildId: string]: number } = {};
    private totalRatioNum = 100;

    private gameIsOver: string | undefined;
    init() {
        // 检测当前兵力，出兵 5分钟
        let checkTime = 0;
        Engine.timeManager.registerTimer(
            TIMER_TASK_TAG.TIMER_GAME_ENEMY_AI_CHECK,
            1 * 3,
            0,
            () => {
                this.checkCitys();
                if (checkTime % 300 == 0) {
                    // 防止卡死
                    this.checkEnemyDispatch();

                    this.checkCanBuildCity();
                }
            },
            undefined,
            this
        );
        // 城市归属改变
        Engine.eventManager.on(
            EVENT_NAME.CHANGE_CITY_BELONG_TYPE,
            (cityId: string) => {
                if (dataManager.dataMap.cityList[cityId].belongTo === BELONG_TYPE.ENEMY_SIDE) {
                    this.checkBuildCity(cityId);
                }

                this.checkCitys();
                // 城市归属改变后，再改变士兵数量。因此此处此城尚无士兵,所以延迟调用
                Engine.timeManager.delayTimeCall(() => {
                    this.checkEnemyDispatch();
                }, 0.1);
            },
            this
        );
        for (const buildId in XLSX.BUILDING) {
            this.buildIdRatio[buildId] = XLSX.BUILDING[buildId].ratio / this.totalRatioNum;
        }
    }

    // ------------------- 检测当前的防守城池和攻击城池-------------------
    // 根据线找出相邻城
    private getNearCitys(cityId: string) {
        const nearCitys: string[] = [];
        for (const lineId in dataManager.dataMap.terrainLines) {
            if (lineId.startsWith(cityId + "-")) {
                nearCitys.push(lineId.replace(cityId + "-", ""));
            } else if (lineId.endsWith("-" + cityId)) {
                nearCitys.push(lineId.replace("-" + cityId, ""));
            }
        }
        return nearCitys;
    }

    // 防守城,集结城,攻击目标城
    private checkCitys() {
        this.attackCitys.clear();
        this.defenseCitys.clear();
        this.noBuildingCitys = [];

        let hasEnemyCity = false;
        const cityList = dataManager.dataMap.cityList;
        for (const cityId in cityList) {
            const cityInfo = cityList[cityId];
            if (cityInfo.belongTo === BELONG_TYPE.ENEMY_SIDE) {
                hasEnemyCity = true;
                // 当前城是敌方，相邻城非敌方；则定义为防守城
                const nearCitys = this.getNearCitys(cityId);
                for (let index = 0; index < nearCitys.length; index++) {
                    const nearCityId = nearCitys[index];
                    if (!cityList[nearCityId] || cityList[nearCityId].belongTo !== BELONG_TYPE.ENEMY_SIDE) {
                        this.defenseCitys.add(cityId);
                        break;
                    }
                }
                if (!dataManager.dataEnemy.buildList[cityId]) {
                    this.noBuildingCitys.push(cityId);
                }
            }
        }

        // 和防守城池临近的敌方城池为集结城
        this.defenseCitys.forEach((cityId) => {
            const nearCitys = this.getNearCitys(cityId);
            let findAttackCity: AttackCityInfo | undefined; // 减少集结城，一个防御城周边选取一个集结城
            let attackTargetCitys: string[] = [];
            nearCitys.forEach((nearCityId) => {
                if (cityList[nearCityId] && cityList[nearCityId].belongTo === BELONG_TYPE.ENEMY_SIDE) {
                    if (!findAttackCity) {
                        findAttackCity = {
                            cityId: nearCityId,
                            defenseCityId: cityId,
                            targetCityIds: [],
                        };
                    }
                } else {
                    attackTargetCitys.push(nearCityId);
                }
            });

            if (findAttackCity === undefined) {
                // 该防守城池周围无己方城池，那么该防守城 既是防守又是集结城池
                findAttackCity = {
                    cityId: cityId,
                    defenseCityId: cityId,
                    targetCityIds: [],
                };
            }
            findAttackCity.targetCityIds = attackTargetCitys;

            this.attackCitys.add(findAttackCity);
        });

        // game over
        if (this.defenseCitys.size === 0 || this.attackCitys.size === 0) {
            this.gameIsOver = !hasEnemyCity ? "win" : "lose";
            Engine.eventManager.emit(EVENT_NAME.GAME_OVER, this.gameIsOver === "win");
            Engine.timeManager.unRegisterTimer(TIMER_TASK_TAG.TIMER_GAME_ENEMY_AI_CHECK);
            return;
        }
        //
        this.checkEnemyAttack();
    }

    // ------------------- 入侵 -------------------
    // 是否触发攻击(1、固定时间检测，2 条件检测--未知)
    private checkEnemyAttack() {
        // 集结城，是否发送攻击
        this.attackCitys.forEach((attackCityInfo) => {
            const randomNum = getRandom();
            // 0.2的概率随机找个城就打，0.8的概率按照规则选择
            if (randomNum < 0.2) {
                this.chooseRandomTargetCityAndDispatchArmy(attackCityInfo);
            } else {
                // 选择原则  0.6概率按兵力，0.4概率距离
                const randomNum = getRandom();
                if (randomNum < 0.6) {
                    this.chooseWeakestTargetCityAndDispatchArmy(attackCityInfo);
                } else {
                    this.chooseClosestTargetCityAndDispatchArmy(attackCityInfo);
                }
            }
        });
    }

    // 随机目标城，并出兵
    private chooseRandomTargetCityAndDispatchArmy(attackCityInfo: AttackCityInfo) {
        const targetCityNum = attackCityInfo.targetCityIds.length;
        const randomIdx = Math.floor(Math.random() * targetCityNum);
        const randomTargetCityId = attackCityInfo.targetCityIds[randomIdx];
        const randomNum = getRandom();
        if (randomNum < 0.5) {
            this.dispatchCityArmy(attackCityInfo.cityId, randomTargetCityId);
        }
    }

    // 检测是否满足出兵条件
    // 1、和敌方目标城池兵力对比,兵力出兵几率
    private chooseWeakestTargetCityAndDispatchArmy(attackCityInfo: AttackCityInfo) {
        const attackCityDetail = dataManager.dataMap.getCityDetail(attackCityInfo.cityId);
        const attackCityArmy: { [soldierId: string]: number } = attackCityDetail!.armyData;
        const attackArmyTypePower = dataManager.dataSoldier.calculateArmyAttackPower(attackCityArmy);
        const attackCityTotalPower = dataManager.dataSoldier.getPowerTotalNum(attackArmyTypePower);
        const { minPowerCityId, minPowerCityPower } = this.findWeakestAttackTargetCity(
            attackCityInfo.targetCityIds,
            attackArmyTypePower
        );
        if (minPowerCityPower < attackCityTotalPower) {
            // 战胜情况下 出兵概率
            const randomNum = getRandom();
            if (randomNum < 0.8) {
                this.dispatchCityArmy(attackCityInfo.cityId, minPowerCityId);
            }
        } else {
            // 战败情况下 出兵概率
            const randomNum = getRandom();
            if (randomNum < 0.4) {
                this.dispatchCityArmy(attackCityInfo.cityId, minPowerCityId);
            }
        }
    }

    // 距离最近出兵几率
    private chooseClosestTargetCityAndDispatchArmy(attackCityInfo: AttackCityInfo) {
        const minLenCityId = this.findClosestAttackTargetCity(
            attackCityInfo.defenseCityId,
            attackCityInfo.targetCityIds
        );
        const randomNum = getRandom();
        if (randomNum < 0.8) {
            this.dispatchCityArmy(attackCityInfo.cityId, minLenCityId);
        }
    }

    // 选择攻击目标城池中战力最弱的
    private findWeakestAttackTargetCity(attackTargetCitys: string[], attackPower: { [type: number]: number }) {
        // 计算防守战力
        let minPowerCityPower = 0;
        let minPowerCityId!: string;
        attackTargetCitys.forEach((cityId) => {
            const targetCityDetail = dataManager.dataMap.getCityDetail(cityId);
            const targetCityArmy: { [soldierId: string]: number } = targetCityDetail!.armyData;
            const defArmyTypePower = dataManager.dataSoldier.calculateArmyDefPower(targetCityArmy, attackPower);
            let targetCityTotal = dataManager.dataSoldier.getPowerTotalNum(defArmyTypePower);
            if (minPowerCityPower === 0) {
                minPowerCityPower = targetCityTotal;
                minPowerCityId = cityId;
            } else if (targetCityTotal < minPowerCityPower) {
                minPowerCityPower = targetCityTotal;
                minPowerCityId = cityId;
            }
        });
        return { minPowerCityId, minPowerCityPower };
    }

    // 选择目标城池中距离最近的
    private findClosestAttackTargetCity(defenseCityId: string, attackTargetCitys: string[]) {
        let minCityLen = 0;
        let minLenCityId!: string;
        attackTargetCitys.forEach((targetCityId) => {
            const len = dataManager.dataMap.dijks!.twoVertexLen(defenseCityId, targetCityId);
            if (minCityLen === 0) {
                minCityLen = len;
                minLenCityId = targetCityId;
            } else if (len < minCityLen) {
                minCityLen = len;
                minLenCityId = targetCityId;
            }
        });

        return minLenCityId;
    }

    // -------------------- 派遣逻辑 --------------------------
    // 是否属于攻击城池
    private isAttackCity(cityId: string) {
        let isAttackCity = false;
        this.attackCitys.forEach((attackCityInfo) => {
            if (cityId === attackCityInfo.cityId) {
                isAttackCity = true;
            }
        });
        return isAttackCity;
    }

    // 是否触发士兵分配(1、固定时间检测，2 条件检测--城池归属改变)
    // 1 防御士兵平局分配到防御城池
    // 2 攻击士兵集中到攻击城池
    private checkEnemyDispatch() {
        const cityList = dataManager.dataMap.cityList;
        for (const cityId in cityList) {
            const cityInfo = cityList[cityId];
            if (cityInfo.belongTo === BELONG_TYPE.ENEMY_SIDE) {
                const isDefenseCity = this.defenseCitys.has(cityId);
                const isAttackCity = this.isAttackCity(cityId);
                if (isAttackCity && isDefenseCity) {
                    // 即是攻击又是防御，按兵不动
                } else if (isDefenseCity) {
                    // 攻击类型的士兵 派往攻击城池
                    this.dispatchToAttckCity(cityId);
                } else if (isAttackCity) {
                    // 防御类型的士兵 派往防御城池
                    this.dispatchToDefenseCity(cityId);
                } else {
                    // 分别派送
                    // TODO 暂时补派遣太乱了
                    this.dispatchToAttckCity(cityId);
                    this.dispatchToDefenseCity(cityId);
                }
            }
        }
    }

    // 派遣到防守城池
    private dispatchToDefenseCity(cityId: string) {
        const randomNum = getRandom();
        let defenseCityId: string;
        if (randomNum < 0.8) {
            defenseCityId = this.findWeakestDefenseCity(cityId);
        } else {
            defenseCityId = this.findClosestDefenseCity(cityId);
        }
        this.dispatchCityArmy(cityId, defenseCityId, ATTACK_OR_DEFENSE.DEFENSE);
    }

    // 派遣到攻击城池
    private dispatchToAttckCity(cityId: string) {
        const randomNum = getRandom();
        let attakCityId: string;
        if (randomNum < 0.5) {
            attakCityId = this.findClosestAttackCity(cityId);
        } else {
            attakCityId = this.findStrongestAttackCity(cityId);
        }
        if (!attakCityId) {
            console.error("无待攻击的城池了");
            return;
        }
        this.dispatchCityArmy(cityId, attakCityId, ATTACK_OR_DEFENSE.ATTACK);
    }

    // 寻找距离城池最近的防守城池
    private findClosestDefenseCity(cityId: string) {
        let minCityLen = 0;
        let minLenCityId!: string;
        this.defenseCitys.forEach((defenseCityId) => {
            const len = dataManager.dataMap.dijks!.twoVertexLen(cityId, defenseCityId);
            if (minCityLen === 0) {
                minCityLen = len;
                minLenCityId = defenseCityId;
            } else if (len < minCityLen) {
                minCityLen = len;
                minLenCityId = defenseCityId;
            }
        });

        return minLenCityId;
    }
    // 寻找最弱的防守城池
    private findWeakestDefenseCity(cityId: string) {
        // 计算防守战力
        let minPowerCityPower = 0;
        let minPowerCityId!: string;
        this.defenseCitys.forEach((defenseCityId) => {
            const targetCityDetail = dataManager.dataMap.getCityDetail(defenseCityId);
            const targetCityArmy: { [soldierId: string]: number } = targetCityDetail!.armyData;
            const defArmyTypePower = dataManager.dataSoldier.calculateArmyDefPower(targetCityArmy);
            let targetCityTotal = dataManager.dataSoldier.getPowerTotalNum(defArmyTypePower);
            if (minPowerCityPower === 0) {
                minPowerCityPower = targetCityTotal;
                minPowerCityId = defenseCityId;
            } else if (targetCityTotal < minPowerCityPower) {
                minPowerCityPower = targetCityTotal;
                minPowerCityId = defenseCityId;
            }
        });
        return minPowerCityId;
    }
    // 寻找最近的攻击城池
    private findClosestAttackCity(cityId: string) {
        let minCityLen = 0;
        let minLenCityId!: string;
        this.attackCitys.forEach((attackCityInfo) => {
            const attakCityId = attackCityInfo.cityId;
            const len = dataManager.dataMap.dijks!.twoVertexLen(cityId, attakCityId);
            if (minCityLen === 0) {
                minCityLen = len;
                minLenCityId = attakCityId;
            } else if (len < minCityLen) {
                minCityLen = len;
                minLenCityId = attakCityId;
            }
        });
        return minLenCityId;
    }
    // 寻找最强的攻击城池
    private findStrongestAttackCity(cityId: string) {
        let maxPowerCityPower = 0;
        let maxPowerCityId!: string;
        this.attackCitys.forEach((attackCityInfo) => {
            const attakCityId = attackCityInfo.cityId;
            const targetCityDetail = dataManager.dataMap.getCityDetail(attakCityId);
            const targetCityArmy: { [soldierId: string]: number } = targetCityDetail!.armyData;
            const attackArmyTypePower = dataManager.dataSoldier.calculateArmyAttackPower(targetCityArmy);
            const attackCityTotalPower = dataManager.dataSoldier.getPowerTotalNum(attackArmyTypePower);
            if (attackCityTotalPower === 0) {
                maxPowerCityPower = attackCityTotalPower;
                maxPowerCityId = attakCityId;
            } else if (attackCityTotalPower > maxPowerCityPower) {
                maxPowerCityPower = attackCityTotalPower;
                maxPowerCityId = attakCityId;
            }
        });
        return maxPowerCityId;
    }

    // ----------------- 建造------------------------
    // 当前应该建造那个建筑
    // 建造规则：按照各自占比判断当前应当建造的建筑
    // 当前数量/建筑平衡的占比  选取最小值建造
    private getCurrentBuildBuildId() {
        const buildIdNumList: { [buildId: string]: number } = {};
        const buildList = dataManager.dataEnemy.buildList;
        for (const cityId in buildList) {
            const buildId = buildList[cityId].cfgId;
            buildIdNumList[buildId] = buildIdNumList[buildId] ? buildIdNumList[buildId] + 1 : 1;
        }

        // 假定下次建造该建筑，建筑后的 数量/建筑平衡的占比
        const nextBuildRatio: { [buildId: string]: number } = {};
        for (const buildId in XLSX.BUILDING) {
            const nextBuidNum = buildIdNumList[buildId] ? buildIdNumList[buildId] + 1 : 1;
            nextBuildRatio[buildId] = nextBuidNum / this.buildIdRatio[buildId];
        }

        let findBuildMinRatio: number | undefined;
        let minRatioBuildId: string | undefined;
        for (const buildId in nextBuildRatio) {
            if (findBuildMinRatio === undefined) {
                findBuildMinRatio = nextBuildRatio[buildId];
                minRatioBuildId = buildId;
            } else if (nextBuildRatio[buildId] < findBuildMinRatio) {
                findBuildMinRatio = nextBuildRatio[buildId];
                minRatioBuildId = buildId;
            }
        }
        return minRatioBuildId;
    }

    // 检测建筑城池建筑
    private checkBuildCity(cityId: string) {
        const nextBuildBuildId = this.getCurrentBuildBuildId();
        if (nextBuildBuildId) {
            const buildRes = dataManager.dataEnemy.addBuilding(cityId, nextBuildBuildId);
            if (!buildRes) {
                this.noBuildingCitys.push(cityId);
            } else {
                Logger.log("less res build city bulding :", cityId);
            }
        }
    }

    // 检测无建筑城市是否满足建造条件
    private checkCanBuildCity() {
        if (this.noBuildingCitys.length > 0) {
            const cityId = this.noBuildingCitys.shift()!;
            this.checkBuildCity(cityId);
        }
    }

    // 出征
    private dispatchCityArmy(
        startCityId: string,
        targetCityId: string,
        attackOrDefense: number = ATTACK_OR_DEFENSE.ATTACK
    ) {
        if (startCityId === targetCityId) {
            console.error("start and target city same:", startCityId);
            return;
        }
        // 选择出征城的 所有攻击属性士兵
        const armyData: { [soldierId: string]: number } = {};
        let dispatchArmyBattleNum = 0; // 出征士兵的相当于的战斗单位数量 <1000则暂不出兵
        for (const soldierId in dataManager.dataMap.cityList[startCityId].soldiers) {
            //  士兵属性配置 攻击属性
            const soldierCfgData = dataManager.dataSoldier.getSoldierCfgData(soldierId);
            if (soldierCfgData.attackOrDefense === attackOrDefense || soldierCfgData.attackOrDefense === 3) {
                const soldierNum = dataManager.dataMap.cityList[startCityId].soldiers[soldierId];
                const realNum = getNumFloor(soldierNum);
                if (realNum > 0) {
                    armyData[soldierId] = armyData[soldierId] ? armyData[soldierId] + realNum : realNum;
                    dispatchArmyBattleNum += realNum * soldierCfgData.battleNum;
                }
            }
        }
        if (dispatchArmyBattleNum < 1000) {
            // 防止出兵过于频繁
            return;
        }

        console.log("startId->toId:", startCityId, targetCityId, dispatchArmyBattleNum);
        if (!targetCityId) {
            console.error("target city is null:", startCityId, targetCityId);
            return;
        }

        dataManager.dataMap.dispatchArmy(startCityId, targetCityId, armyData, BELONG_TYPE.ENEMY_SIDE);
    }
}

export const emenyAI = EnemyAI.instance;
